package trash_pck;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;

public class IteratorTest {
	public static void main(String[] args) {
		int[] relativeSizes = new int[] { 50000 };
		int numTimesToTestEachLoop = 1000;
		for (int relativeSize : relativeSizes) {
			System.out.println("real size = " + relativeSize*6);
			Timer timer = new Timer();
			String[] s = new String[] { "foo", "bar", "baz", "larry", "moe", "curly" };
			String[] array = new String[relativeSize * 6];
			for (int i = 0; i < array.length; i++) {
				array[i] = s[i % 6];
			}
			ArrayList<String> oldArrayList = new ArrayList<String>(Arrays.asList(new String[] {
					"foo", "bar", "baz", "larry", "moe", "curly" }));
			ArrayList<String> arrayList = new ArrayList<String>();

			// make the arr huge; copy it several times
			for (int i = 0; i < relativeSize; i++) {
				arrayList.addAll(oldArrayList);
			}
			System.out.print("testing loop on the array alone : ");
			double totalTime = 0.0;
			double[] eachTime = new double[numTimesToTestEachLoop];
			for (int j = 0; j < numTimesToTestEachLoop; j++) {
				Object[] arr = arrayList.toArray();
				timer.start();
				for (int i = arr.length - 1; i >= 0; i--) {
					((String) arr[i]).hashCode();
				}
				eachTime[j] = timer.stop();
				totalTime += eachTime[j];
			}
			System.out.println("avg = " + (totalTime / numTimesToTestEachLoop) + "ms, var = "
					+ getStdDev(eachTime, totalTime / numTimesToTestEachLoop) + "ms, min = "
					+ getMin(eachTime) + "ms, max = " + getMax(eachTime) + "ms.");
			System.out.print("testing .toArray() and basic for-loop on the array : ");
			totalTime = 0.0;
			for (int j = 0; j < numTimesToTestEachLoop; j++) {
				timer.start();
				Object[] arr = arrayList.toArray();
				for (int i = arr.length - 1; i >= 0; i--) {
					((String) arr[i]).hashCode();
				}
				eachTime[j] = timer.stop();
				totalTime += eachTime[j];
			}
			System.out.println("avg = " + (totalTime / numTimesToTestEachLoop) + "ms, var = "
					+ getStdDev(eachTime, totalTime / numTimesToTestEachLoop) + "ms, min = "
					+ getMin(eachTime) + "ms, max = " + getMax(eachTime) + "ms.");
			System.out.print("testing ArrayList with basic for-loop : ");
			totalTime = 0.0;
			for (int j = 0; j < numTimesToTestEachLoop; j++) {
				timer.start();
				for (int i = arrayList.size() - 1; i >= 0; i--) {
					arrayList.get(i).hashCode();
				}
				eachTime[j] = timer.stop();
				totalTime += eachTime[j];
			}
			System.out.println("avg = " + (totalTime / numTimesToTestEachLoop) + " ms, var = "
					+ getStdDev(eachTime, totalTime / numTimesToTestEachLoop) + "ms, min = "
					+ getMin(eachTime) + "ms, max = " + getMax(eachTime) + "ms.");
			System.out.print("testing LinkedList with for-each : ");
			LinkedList<String> linkedList = new LinkedList<String>(arrayList);
			totalTime = 0.0;
			for (int j = 0; j < numTimesToTestEachLoop; j++) {
				timer.start();
				for (String str : linkedList) {
					str.hashCode();
				}
				eachTime[j] = timer.stop();
				totalTime += eachTime[j];
			}
			System.out.println("avg = " + (totalTime / numTimesToTestEachLoop) + "ms, var = "
					+ getStdDev(eachTime, totalTime / numTimesToTestEachLoop) + "ms, min = "
					+ getMin(eachTime) + "ms, max = " + getMax(eachTime) + "ms.");
			System.out.print("testing LinkedList with iterator : ");
			totalTime = 0.0;
			for (int j = 0; j < numTimesToTestEachLoop; j++) {
				timer.start();
				Iterator<String> itr = linkedList.iterator();
				for (;;) {
					try {
						itr.next().hashCode();
					} catch (NoSuchElementException e) {
						break;
					}
				}
				eachTime[j] = timer.stop();
				totalTime += eachTime[j];
			}
			System.out.println("avg = " + (totalTime / numTimesToTestEachLoop) + "ms, var = "
					+ getStdDev(eachTime, totalTime / numTimesToTestEachLoop) + "ms, min = "
					+ getMin(eachTime) + "ms, max = " + getMax(eachTime) + "ms.");
			System.out.print("testing LinkedList with iterator and hasNext : ");
			totalTime = 0.0;
			for (int j = 0; j < numTimesToTestEachLoop; j++) {
				timer.start();
				Iterator<String> itr = linkedList.iterator();
				while(itr.hasNext()) {
					itr.next().hashCode();
				}
				eachTime[j] = timer.stop();
				totalTime += eachTime[j];
			}
			System.out.println("avg = " + (totalTime / numTimesToTestEachLoop) + "ms, var = "
					+ getStdDev(eachTime, totalTime / numTimesToTestEachLoop) + "ms, min = "
					+ getMin(eachTime) + "ms, max = " + getMax(eachTime) + "ms.");
			System.out.print("testing ArrayList with for-each : ");
			totalTime = 0.0;
			for (int j = 0; j < numTimesToTestEachLoop; j++) {
				timer.start();
				for (String str : arrayList) {
					str.hashCode();
				}
				eachTime[j] = timer.stop();
				totalTime += eachTime[j];
			}
			System.out.println("avg = " + (totalTime / numTimesToTestEachLoop) + "ms, var = "
					+ getStdDev(eachTime, totalTime / numTimesToTestEachLoop) + "ms, min = "
					+ getMin(eachTime) + "ms, max = " + getMax(eachTime) + "ms.");
			System.out.print("testing ArrayList with iterator : ");
			totalTime = 0.0;
			for (int j = 0; j < numTimesToTestEachLoop; j++) {
				timer.start();
				Iterator<String> itr = arrayList.iterator();
				for (;;) {
					try {
						itr.next().hashCode();
					} catch (NoSuchElementException e) {
						break;
					}
				}
				eachTime[j] = timer.stop();
				totalTime += eachTime[j];
			}
			System.out.println("avg = " + (totalTime / numTimesToTestEachLoop) + "ms, var = "
					+ getStdDev(eachTime, totalTime / numTimesToTestEachLoop) + "ms, min = "
					+ getMin(eachTime) + "ms, max = " + getMax(eachTime) + "ms.");
			System.out.print("testing loop on the array : ");
			totalTime = 0.0;
			eachTime = new double[numTimesToTestEachLoop];
			for (int j = 0; j < numTimesToTestEachLoop; j++) {
				timer.start();
				for (int i = array.length - 1; i >= 0; i--) {
					((String) array[i]).hashCode();
				}
				eachTime[j] = timer.stop();
				totalTime += eachTime[j];
			}
			System.out.println("avg = " + (totalTime / numTimesToTestEachLoop) + "ms, var = "
					+ getStdDev(eachTime, totalTime / numTimesToTestEachLoop) + "ms, min = "
					+ getMin(eachTime) + "ms, max = " + getMax(eachTime) + "ms.");

			System.out.println("\n----------------\n");
			// let the system garbage collect for awhile
			System.gc();
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
			}
		}
	}

	public static double getStdDev(double[] results, double avg) {
		double sum = 0;
		for (int i = 0; i < results.length; i++) {
			sum += (results[i] - avg) * (results[i] - avg);
		}
		return Math.sqrt(sum / results.length);
	}

	public static double getMax(double[] array) {
		double max = Double.NEGATIVE_INFINITY;
		for (int i = 0; i < array.length; i++) {
			if (array[i] > max)
				max = array[i];
		}
		return max;
	}

	public static double getMin(double[] array) {
		double min = Double.POSITIVE_INFINITY;
		for (int i = 0; i < array.length; i++) {
			if (array[i] < min)
				min = array[i];
		}
		return min;
	}

	/**
	 * simple start-stop timer
	 */
	private static class Timer {
		private long savedTime;

		public void start() {
			savedTime = System.currentTimeMillis();
		}

		public long stop() {
			try {
				return System.currentTimeMillis() - savedTime;
			} catch (NullPointerException e) { // start wasn't called
				throw new IllegalStateException("call start() first", e);
			}
		}
	}
}
